Implement the `links` manifest key
authorAlex Crichton <alex@alexcrichton.com>
Fri, 31 Oct 2014 17:49:04 +0000 (10:49 -0700)
committerAlex Crichton <alex@alexcrichton.com>
Wed, 5 Nov 2014 19:37:34 +0000 (11:37 -0800)
This commit adds the `links` manifest key as a string of a C library which is
being linked to. This is passed as an argument to the build command when not
overridden. The implementation of overrides will come soon!

src/cargo/core/manifest.rs
src/cargo/ops/cargo_rustc/custom_build.rs
src/cargo/ops/cargo_rustc/links.rs [new file with mode: 0644]
src/cargo/ops/cargo_rustc/mod.rs
src/cargo/util/toml.rs
tests/test_cargo_compile_custom_build.rs

index 666790746152e7597241d8c36db45c23f779df3c..ca40de4c0165bdcc8b5b77c3d7fdfa7939bb89a0 100644 (file)
@@ -17,6 +17,7 @@ pub struct Manifest {
     target_dir: Path,
     doc_dir: Path,
     build: Vec<String>,         // TODO: deprecated, remove
+    links: Option<String>,
     warnings: Vec<String>,
     exclude: Vec<String>,
     metadata: ManifestMetadata,
@@ -328,7 +329,7 @@ impl<H: hash::Writer> hash::Hash<H> for Profile {
             env: _,
             test: _,
             doctest: _,
-            
+
             custom_build: _,
         } = *self;
         (opt_level, codegen_units, debug, rpath, for_host, dest, harness).hash(into)
@@ -383,7 +384,7 @@ impl Show for Target {
 impl Manifest {
     pub fn new(summary: Summary, targets: Vec<Target>,
                target_dir: Path, doc_dir: Path,
-               build: Vec<String>, exclude: Vec<String>,
+               build: Vec<String>, exclude: Vec<String>, links: Option<String>,
                metadata: ManifestMetadata) -> Manifest {
         Manifest {
             summary: summary,
@@ -393,6 +394,7 @@ impl Manifest {
             build: build,     // TODO: deprecated, remove
             warnings: Vec::new(),
             exclude: exclude,
+            links: links,
             metadata: metadata,
         }
     }
@@ -433,6 +435,10 @@ impl Manifest {
         self.build.as_slice()
     }
 
+    pub fn get_links(&self) -> Option<&str> {
+        self.links.as_ref().map(|s| s.as_slice())
+    }
+
     pub fn add_warning(&mut self, s: String) {
         self.warnings.push(s)
     }
index 1fb19f763f3fd09d59598825c0651226246520fa..55c02232720e0e88117808a76bb3d8f41fea0a84 100644 (file)
@@ -94,7 +94,7 @@ pub fn prepare_execute_custom_build(pkg: &Package, target: &Target,
         let output = try!(p.exec_with_output().map_err(|mut e| {
             e.msg = format!("Failed to run custom build command for `{}`\n{}",
                             pkg, e.msg);
-            e.mark_human()
+            e.concrete().mark_human()
         }));
 
         // parsing the output of the custom build script to check that it's correct
diff --git a/src/cargo/ops/cargo_rustc/links.rs b/src/cargo/ops/cargo_rustc/links.rs
new file mode 100644 (file)
index 0000000..dbb6952
--- /dev/null
@@ -0,0 +1,37 @@
+use std::collections::HashMap;
+
+use core::PackageSet;
+use util::{CargoResult, human};
+
+// Returns a mapping of the root package plus its immediate dependencies to
+// where the compiled libraries are all located.
+pub fn validate(deps: &PackageSet) -> CargoResult<()> {
+    let mut map = HashMap::new();
+
+    for dep in deps.iter() {
+        let lib = match dep.get_manifest().get_links() {
+            Some(lib) => lib,
+            None => continue,
+        };
+        match map.find(&lib) {
+            Some(previous) => {
+                return Err(human(format!("native library `{}` is being linked \
+                                          to by more than one package, and \
+                                          can only be linked to by one \
+                                          package\n\n  {}\n  {}",
+                                         lib, previous, dep.get_package_id())))
+            }
+            None => {}
+        }
+        if !dep.get_manifest().get_targets().iter().any(|t| {
+            t.get_profile().is_custom_build()
+        }) {
+            return Err(human(format!("package `{}` specifies that it links to \
+                                      `{}` but does not have a custom build \
+                                      script", dep.get_package_id(), lib)))
+        }
+        map.insert(lib, dep.get_package_id());
+    }
+
+    Ok(())
+}
index 65db067d4f083b891018fbc246adb068a5686cea..4691fdc70a7c2b2a85e0fe3f0be11a3032439700 100644 (file)
@@ -26,6 +26,7 @@ mod fingerprint;
 mod job;
 mod job_queue;
 mod layout;
+mod links;
 
 #[deriving(PartialEq, Eq)]
 pub enum Kind { KindHost, KindTarget }
@@ -83,6 +84,8 @@ pub fn compile_targets<'a>(env: &str, targets: &[&'a Target], pkg: &'a Package,
 
     debug!("compile_targets; targets={}; pkg={}; deps={}", targets, pkg, deps);
 
+    try!(links::validate(deps));
+
     let dest = uniq_target_dest(targets);
     let root = deps.iter().find(|p| p.get_package_id() == resolve.root()).unwrap();
     let host_layout = Layout::new(root, None, dest);
index 82435718b59a19569a87ad8a9e48f6e534c99734..3c98df6df41e73d878b3f98911be1338ab35bbce 100644 (file)
@@ -252,6 +252,7 @@ pub struct TomlProject {
     version: TomlVersion,
     authors: Vec<String>,
     build: Option<TomlBuildCommandsList>,       // TODO: `String` instead
+    links: Option<String>,
     exclude: Option<Vec<String>>,
 
     // package metadata
@@ -514,6 +515,7 @@ impl TomlManifest {
                                          layout.root.join("doc"),
                                          old_build,
                                          exclude,
+                                         project.links.clone(),
                                          metadata);
         if used_deprecated_lib {
             manifest.add_warning(format!("the [[lib]] section has been \
index 9332dccb9fe7abb34ceac806a2083ff4ccb818dc..db4613abd8d8de9e4bdf36007bec4cfd8df42488 100644 (file)
@@ -1,5 +1,3 @@
-use std::path;
-
 use support::{project, execs};
 use support::{COMPILING, RUNNING};
 use hamcrest::{assert_that};
@@ -188,3 +186,61 @@ url = p.url(),
 )));
 })
 */
+
+test!(links_no_build_cmd {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.5.0"
+            authors = []
+            links = "a"
+        "#)
+        .file("src/lib.rs", "");
+
+    assert_that(p.cargo_process("build"),
+                execs().with_status(101)
+                       .with_stderr("\
+package `foo v0.5.0 (file://[..])` specifies that it links to `a` but does \
+not have a custom build script
+"));
+})
+
+
+test!(links_duplicates {
+    let p = project("foo")
+        .file("Cargo.toml", r#"
+            [project]
+            name = "foo"
+            version = "0.5.0"
+            authors = []
+            links = "a"
+            build = "build.rs"
+
+            [dependencies.a]
+            path = "a"
+        "#)
+        .file("src/lib.rs", "")
+        .file("build.rs", "")
+        .file("a/Cargo.toml", r#"
+            [project]
+            name = "a"
+            version = "0.5.0"
+            authors = []
+            links = "a"
+            build = "build.rs"
+        "#)
+        .file("a/src/lib.rs", "")
+        .file("a/build.rs", "");
+
+    assert_that(p.cargo_process("build"),
+                execs().with_status(101)
+                       .with_stderr("\
+native library `a` is being linked to by more than one package, and can only be \
+linked to by one package
+
+  foo v0.5.0 (file://[..])
+  a v0.5.0 (file://[..])
+"));
+})
+